﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace JWBST.CommonClass
{
    public static class JsonUtils
    {
        public const char EscapeChar = '\\';
        public const char QuoteChar = '"';
        public const string Null = "null";
        public const string True = "true";
        public const string False = "false";

        static readonly char[] EscapeChars = new[]
			{
				QuoteChar, '\n', '\r', '\t', '"', '\\', '\f', '\b',
			};

        private const int LengthFromLargestChar = '\\' + 1;
        private static readonly bool[] EscapeCharFlags = new bool[LengthFromLargestChar];

        static JsonUtils()
        {
            foreach (var escapeChar in EscapeChars)
            {
                EscapeCharFlags[escapeChar] = true;
            }
        }

        public static string ConvertJsonString(string value)
        {
            System.Text.StringBuilder writer = new System.Text.StringBuilder();
            if (!HasAnyEscapeChars(value))
            {
                writer.Append(QuoteChar);
                writer.Append(value);
                writer.Append(QuoteChar);
                return writer.ToString();
            }

            var hexSeqBuffer = new char[4];
            writer.Append(QuoteChar);

            var len = value.Length;
            for (var i = 0; i < len; i++)
            {
                switch (value[i])
                {
                    case '\n':
                        writer.Append("\\n");
                        continue;

                    case '\r':
                        writer.Append("\\r");
                        continue;

                    case '\t':
                        writer.Append("\\t");
                        continue;

                    case '"':
                    case '\\':
                        writer.Append('\\');
                        writer.Append(value[i]);
                        continue;

                    case '\f':
                        writer.Append("\\f");
                        continue;

                    case '\b':
                        writer.Append("\\b");
                        continue;         
                }

                //Is printable char?
                if (value[i] >= 32 && value[i] <= 126)
                {
                    writer.Append(value[i]);
                    continue;
                }

                // Default, turn into a \uXXXX sequence
                IntToHex(value[i], hexSeqBuffer);
                writer.Append("\\u");
                writer.Append(hexSeqBuffer);
            }

            writer.Append(QuoteChar);
            string m = writer.ToString().Replace("\r", "").Replace("\n", "").Trim();
            return m;
        }

        /// <summary>
        /// micro optimizations: using flags instead of value.IndexOfAny(EscapeChars)
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static bool HasAnyEscapeChars(string value)
        {
            var len = value.Length;
            for (var i = 0; i < len; i++)
            {
                var c = value[i];
                if (c >= LengthFromLargestChar || !EscapeCharFlags[c]) continue;
                return true;
            }
            return false;
        }

        public static void IntToHex(int intValue, char[] hex)
        {
            for (var i = 0; i < 4; i++)
            {
                var num = intValue % 16;

                if (num < 10)
                    hex[3 - i] = (char)('0' + num);
                else
                    hex[3 - i] = (char)('A' + (num - 10));

                intValue >>= 4;
            }
        }
    }
}